package comms;

import javax.microedition.io.Connector;
import javax.microedition.io.DatagramConnection;
import javax.microedition.io.Datagram;
import ap.AgentPkt;

import java.io.IOException;

public class DatagramAgentConnection extends Thread implements AgentConnection {
	private ConnectionManager owner;
	private DatagramConnection currentConnection;
	private int connectionStatus;
	private int maximumLength;
	private boolean listening;

	/**
	 * Constructor for a connection which listens for Datagrams on the 
	 * supplied port.
	 * 
	 * @param Port The port to listen on.
	 */
	public DatagramAgentConnection(ConnectionManager o, int port) {
		this.owner = o;
		this.connectionStatus = AgentConnection.RECEIVER_ONLY;
		
		try {
			this.currentConnection = (DatagramConnection)Connector.open("datagram://:" + port);
			this.maximumLength = this.currentConnection.getMaximumLength();
		} catch (IOException e) {
			e.printStackTrace();
		}
			
		this.listening = true;
		this.start();
	}
	
	/**
	 * Constructor for a connection which can be used to send agents via 
	 * UDP to the supplied address:port.
	 * 
	 * @param Port The port to send over.
	 * @param address The address to send to.
	 */
	public DatagramAgentConnection(ConnectionManager o, int port, String address) {
		this.owner = o;
		this.connectionStatus = AgentConnection.SENDER_ONLY;
		this.listening = false;
		
		//TODO: ensure port and address is verified.
		try {
			this.currentConnection = (DatagramConnection)Connector.open("datagram://" + address + ":" + port);
			this.maximumLength = this.currentConnection.getMaximumLength();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Temporary overloaded constructor for altered ConnectionManager methods.
	 * 
	 * @param o
	 * @param address
	 * @param port
	 */
	public DatagramAgentConnection(ConnectionManager o, String address, String port) {
		this.owner = o;
		
		try {
			this.currentConnection = (DatagramConnection)Connector.open("datagram://" + address + ":" + port);
			this.maximumLength = this.currentConnection.getMaximumLength();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if (address.length() > 0) {
			this.connectionStatus = AgentConnection.SENDER_ONLY;
			this.listening = false;
		} else {
			this.connectionStatus = AgentConnection.RECEIVER_ONLY;
			this.listening = true;
			this.start();
		}	

	}
	
	/**
	 * Run method for this Thread; uses the global RUN_DELAY and then repeatedly
	 * listens for incoming packets if required.
	 */
	public void run() {
		Datagram datagram = null;
		try {
			datagram = this.currentConnection.newDatagram(this.maximumLength);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		while (this.listening && (datagram != null)) {
			try {
				System.err.println("listening");
				this.currentConnection.receive(datagram);
				System.err.println(new String(datagram.getData(), 0, datagram.getLength()));
				this.owner.agentReceived(datagram.getData(), datagram.getLength());
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}
	}
	
	public boolean sendAgent(MobileAgent agent) throws AgentConnectionException {
		if ((this.connectionStatus == AgentConnection.RECEIVER_ONLY) ||
			(this.connectionStatus == AgentConnection.CLOSED))
			throw new AgentConnectionException("Connection closed or not in send mode.");
		
		Datagram datagram = null;
		try {
			datagram = this.currentConnection.newDatagram(this.maximumLength);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//TODO: implement using persistance interface
		if (datagram != null) {
			//test
			try {
				datagram.write(agent.getDescription().getBytes());
				this.currentConnection.send(datagram);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return true;
			//endtest
		} else
			return false;
	}
	
	public boolean sendAgent(AgentPkt agentPack) throws AgentConnectionException {
		if ((this.connectionStatus == AgentConnection.RECEIVER_ONLY) ||
				(this.connectionStatus == AgentConnection.CLOSED))
				throw new AgentConnectionException("Connection closed or not in send mode.");
			
			Datagram datagram = null;
			try {
				datagram = this.currentConnection.newDatagram(this.maximumLength);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			if (datagram != null) {
				try {
					datagram.write(agentPack.getAgentString().getBytes());
					this.currentConnection.send(datagram);
				} catch (IOException e) {
					e.printStackTrace();
				}
				return true;
			} else
				return false;
	}
	
	public int getStatus() {
		return this.connectionStatus;
	}
	
	/**
	 * Closes this connection and attempts to ensure any Threads currently
	 * using this object can finish correctly.
	 */
	public synchronized void closeConnection() {
		this.listening = false;
		this.connectionStatus = AgentConnection.CLOSED;
		
		try {
			this.currentConnection.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
